package com.neo.tiny.search.es.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.neo.tiny.common.util.CommonDoTransfer;
import com.neo.tiny.search.es.domain.db.EsPreServiceDO;
import com.neo.tiny.search.es.domain.es.EsSearchPre;
import com.neo.tiny.search.es.repository.EsPreRepository;
import com.neo.tiny.search.es.service.EsPreService;
import com.neo.tiny.search.es.service.EsPreServiceService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: yqz
 * @CreateDate: 2022/10/20 23:16
 */
@Slf4j
@Service
public class EsPreServiceImpl implements EsPreService {

    @Autowired
    private EsPreServiceService preServiceService;

    @Autowired
    private EsPreRepository esPreRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public int importAllData() {
        List<EsPreServiceDO> all = preServiceService.list();

        List<EsSearchPre> transfer = CommonDoTransfer.transfer(all, EsSearchPre.class);
        createIndexMapping(EsSearchPre.class);
        Iterable<EsSearchPre> iterable = esPreRepository.saveAll(transfer);
        Iterator<EsSearchPre> iterator = iterable.iterator();
        int result = 0;
        while (iterator.hasNext()) {
            result++;
            iterator.next();
        }
        return result;
    }

    /**
     * 初始化数据时创建映射
     *
     * @param clz es 实体类
     * @param <T> 范型约束
     */
    private <T> void createIndexMapping(Class<T> clz) {
        //创建索引 可以指定在postconstruct内
        IndexOperations ops = elasticsearchRestTemplate.indexOps(clz);
        if (ops.exists()) {
            ops.delete();
        }
        ops.create();
        ops.refresh();
        Map<String, Object> settings = ops.getSettings();
        log.info("es 索引setting设置详情：{}", JSONUtil.toJsonStr(settings));
        Document mapping = ops.createMapping();
        log.info("es 索引setting 映射文件：{}", JSONUtil.toJsonStr((mapping)));
        ops.putMapping(mapping);
    }


    @Override
    public Page<EsSearchPre> highLightSearch(String keyword, Integer pageNum, Integer pageSize) {

        // 分页
        Pageable pageable = PageRequest.of(pageNum, pageSize);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        Optional.ofNullable(keyword).ifPresent(kw ->
                queryBuilder.withQuery(QueryBuilders.multiMatchQuery(kw, "serviceName", "keyWords")));

        // 配置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder()
                .field("serviceName")
                // 关闭多处高亮
                .requireFieldMatch(false)
                .preTags("<span style=color:red>")
                .postTags("</span>");

        // 构建搜索条件
        NativeSearchQuery query = queryBuilder.withHighlightBuilder(highlightBuilder).withPageable(pageable).build();

        log.info("es-DSL:【{}】", query.getQuery());
        SearchHits<EsSearchPre> resList = elasticsearchRestTemplate.search(query, EsSearchPre.class);


        if (resList.getTotalHits() == 0) {
            return new PageImpl<>(new ArrayList<>(), pageable, 0);
        }

        List<EsSearchPre> preList = resList.getSearchHits().stream().map(hit -> {
            Map<String, List<String>> highlightFields = hit.getHighlightFields();
            EsSearchPre searchPre = hit.getContent();

            List<String> serviceNames = highlightFields.get("serviceName");
            if (CollUtil.isNotEmpty(serviceNames)) {
                // 将添加高亮span标签的目标字段重新放回对象
                serviceNames.forEach(searchPre::setHighLightServiceName);
            }
            return searchPre;
        }).collect(Collectors.toList());
        return new PageImpl<>(preList, pageable, resList.getTotalHits());
    }

    @Override
    public Collection<String> listAllIndex() throws IOException {


        GetIndexRequest indexRequest = new GetIndexRequest("*");
        GetIndexResponse indexResponse = restHighLevelClient.indices().get(indexRequest, RequestOptions.DEFAULT);

        return Arrays.asList(indexResponse.getIndices());
    }

    @Override
    public Boolean delIndex(String index) throws IOException {

        DeleteIndexRequest request = new DeleteIndexRequest(index);
        AcknowledgedResponse delete = restHighLevelClient
                .indices()
                .delete(request, RequestOptions.DEFAULT);
        return delete.isAcknowledged();
    }

    @Override
    public Page<EsSearchPre> search(String keyword, Integer pageNum, Integer pageSize) {

        Pageable pageable = PageRequest.of(pageNum, pageSize);
        return esPreRepository.findByServiceNameOrKeyWordsOrServiceCode(keyword, keyword, keyword, pageable);
    }
}
